<?php

namespace app\service\http\yzt;

use app\model\user\UserModel;
use hunma\exception\ApiException;
use hunma\utils\guzzle\BaseHttpClient;
use support\Log;
use support\Redis;

class YztHttpClient extends BaseHttpClient
{
    /**
     * Redis键前缀
     */
    const REDIS_KEY_PREFIX = 'yzt_user_login_';

    /**
     * Token缓存时间（秒）
     */
    const TOKEN_CACHE_TTL = 360000; // 100小时

    /**
     * 最大重试次数
     */
    const MAX_RETRIES = 3;

    /**
     * 终端类型
     */
    const TERMINAL_TYPE = 9;

    /**
     * 客户端ID
     */
    const CLIENT_ID = 'yzt';

    /**
     * 免登录接口列表（接口路径前缀）
     */
    const NO_LOGIN_ENDPOINTS = [
        '/api/login',
        // 可以继续添加其他免登录接口
    ];

    /**
     * API 密钥
     * @var string
     */
    protected $apiKey;

    /**
     * API 基础URL
     * @var string
     */
    protected $baseUrl;

    /**
     * 当前用户account
     * @var string
     */
    protected $currentAccount;

    /**
     * 静态存储当前用户account
     * @var string
     */
    protected static $staticAccount = '';

    /**
     * 构造函数
     *
     * @param array $config HTTP客户端配置
     * @param string|null $account 用户账号
     */
    public function __construct(array $config = [], $account = null)
    {
        $this->apiKey = getenv('YZT_API_KEY', 'usuw');
        $this->baseUrl = getenv('YZT_API_URL', 'https://api.czt.to');
        $this->currentAccount = $account ?? self::$staticAccount;

        // 初始化默认配置
        $config = $this->initConfig($config);

        parent::__construct($config);

        // 设置默认查询参数包含API密钥
        $this->defaultOptions['query']['key'] = $this->apiKey;
    }

    /**
     * 初始化配置
     *
     * @param array $config 原始配置
     * @return array 处理后的配置
     */
    protected function initConfig(array $config): array
    {
        // 合并默认头部信息
        $defaultHeaders = [
            'X-Client-ID' => self::CLIENT_ID,
            'X-Request-Time' => date('Y-m-d H:i:s'),
            'terminal' => self::TERMINAL_TYPE,
        ];

        $config['headers'] = array_merge($defaultHeaders, $config['headers'] ?? []);

        return $config;
    }

    /**
     * 设置静态当前用户account
     *
     * @param string $account
     */
    public static function setStaticAccount($account): void
    {
        self::$staticAccount = $account;
    }

    /**
     * 获取静态当前用户account
     *
     * @return string
     */
    public static function getStaticAccount(): string
    {
        return self::$staticAccount;
    }

    /**
     * 发送GET请求
     *
     * @param string $endpoint API端点
     * @param array $params 查询参数
     * @param array $headers 请求头
     * @param array $options 请求选项
     * @return array 响应数据
     */
    public function get($endpoint, array $params = [], array $headers = [], array $options = []): array
    {
        return $this->requestWithTokenRetry('GET', $endpoint, $params, $headers, $options);
    }

    /**
     * 发送POST请求
     *
     * @param string $endpoint API端点
     * @param array $data 请求数据
     * @param array $headers 请求头
     * @param array $options 请求选项
     * @return array 响应数据
     */
    public function post($endpoint, array $data = [], array $headers = [], array $options = []): array
    {
        return $this->requestWithTokenRetry('POST', $endpoint, $data, $headers, $options);
    }

    /**
     * 统一处理token、重试和签名
     *
     * @param string $method HTTP方法
     * @param string $endpoint API端点
     * @param array $params 请求参数
     * @param array $headers 请求头
     * @param array $options 请求选项
     * @return array 响应数据
     * @throws ApiException 请求异常
     */
    protected function requestWithTokenRetry($method, $endpoint, array $params = [], array $headers = [], array $options = []): array
    {
        $url = $this->buildUrl($endpoint);
        $account = $this->extractAccount($params, $headers);

        // 添加签名
        $params = $this->addSignature($params);

        $try = 0;
        $maxTries = self::MAX_RETRIES;
        $response = [];

        do {
            $try++;
            try {
                // 处理token（非登录接口）
                $headers = $this->processToken($headers, $account, $endpoint);
                // 发送请求
                $response = $this->sendRequest($method, $url, $params, $headers, $options);
                // 处理可能的token失效情况
                if ($this->isTokenInvalid($response, $account, $endpoint)) {
                    $this->handleInvalidToken($account);
                    unset($headers['token']);
                    continue; // 重试
                }

                // 成功获取响应，退出循环
                break;
            } catch (\Exception $e) {
                Log::channel('guzzlehttp')->error("Yzt API请求异常", [
                    'exception' => $e->getMessage(),
                    'endpoint' => $endpoint,
                    'params' => $params,
                    'try' => $try
                ]);

                if ($try >= $maxTries) {
                    throw new ApiException('请求yzt报错：' . $e->getMessage());
                }
            }
        } while ($try < $maxTries);

        return $response;
    }

    /**
     * 从参数或头部提取账号
     *
     * @param array $params 请求参数
     * @param array $headers 请求头
     * @return string|null 账号
     */
    protected function extractAccount(array $params, array $headers): ?string
    {
        return $params['account'] ?? ($headers['account'] ?? null);
    }

    /**
     * 添加签名到参数
     *
     * @param array $params 原始参数
     * @return array 添加签名后的参数
     */
    protected function addSignature(array $params): array
    {
        $params['_timestamp'] = time();
        $params['_sign'] = hash_hmac('sha256', $params['_timestamp'], $this->apiKey);
        return $params;
    }

    /**
     * 处理token
     *
     * @param array $headers 请求头
     * @param string|null $account 账号
     * @param string $endpoint API端点
     * @return array 处理后的请求头
     */
    protected function processToken(array $headers, ?string $account, string $endpoint): array
    {
        // 检查是否为免登录接口
        if (!$this->isNoLoginEndpoint($endpoint) && !isset($headers['token']) && !empty($account)) {
            $headers['token'] = $this->login($account);
        }
        return $headers;
    }

    /**
     * 检查是否为免登录接口
     *
     * @param string $endpoint API端点
     * @return bool 是否为免登录接口
     */
    protected function isNoLoginEndpoint(string $endpoint): bool
    {
        foreach (self::NO_LOGIN_ENDPOINTS as $prefix) {
            if (strpos($endpoint, $prefix) === 0) {
                return true;
            }
        }
        return false;
    }

    /**
     * 发送HTTP请求
     *
     * @param string $method HTTP方法
     * @param string $url 请求URL
     * @param array $params 请求参数
     * @param array $headers 请求头
     * @param array $options 请求选项
     * @return array 响应数据
     */
    protected function sendRequest(string $method, string $url, array $params, array $headers, array $options): array
    {
        if ($method === 'GET') {
            return parent::get($url, $params, $headers, $options);
        } else {
            return parent::postJson($url, $params, $headers, $options);
        }
    }

    /**
     * 检查token是否无效
     *
     * @param array $response 响应数据
     * @param string|null $account 账号
     * @param string $endpoint API端点
     * @return bool token是否无效
     */
    protected function isTokenInvalid(array $response, ?string $account, string $endpoint): bool
    {
        return isset($response['code'])
            && $response['code'] == 401
            && $account
            && !$this->isNoLoginEndpoint($endpoint);
    }

    /**
     * 处理无效token
     *
     * @param string $account 账号
     */
    protected function handleInvalidToken(string $account): void
    {
        Redis::del(self::REDIS_KEY_PREFIX . $account);
    }

    /**
     * 构建完整的API URL
     *
     * @param string $endpoint API端点
     * @return string 完整URL
     */
    protected function buildUrl(string $endpoint): string
    {
        // 如果端点已经是完整URL，则直接返回
        if (strpos($endpoint, 'http://') === 0 || strpos($endpoint, 'https://') === 0) {
            return $endpoint;
        }

        // 确保端点以斜杠开头
        if (strpos($endpoint, '/') !== 0) {
            $endpoint = '/' . $endpoint;
        }

        return $this->baseUrl . $endpoint;
    }

    /**
     * 处理API特定的响应解析
     * 重写父类方法以适应API的特定响应格式
     *
     * @param string $body 响应体
     * @return array|null 解析后的响应数据
     */
    protected function parseJsonResponse($body)
    {
        $data = parent::parseJsonResponse($body);

        // 检查API特有的错误格式
        if (isset($data['error'])) {
            // 记录详细的错误信息
            Log::channel('guzzlehttp')->error("Yzt API错误", [
                'error' => $data['error'],
                'time' => date('Y-m-d H:i:s')
            ]);
        }

        return $data;
    }

    /**
     * 用户登录并获取token
     *
     * @param string $username 用户名
     * @return string 登录token
     * @throws ApiException 登录异常
     */
    protected function login(string $username): string
    {
        // 验证用户名
        if (empty($username)) {
            throw new ApiException('用户名不能为空');
        }

        // 尝试从缓存获取token
        $cachedToken = $this->getTokenFromCache($username);
        if ($cachedToken) {
            return $cachedToken;
        }

        // 从数据库获取用户信息
        $user = $this->getUserFromDatabase($username);

        // 准备登录参数
        $requestParams = $this->prepareLoginParams($user);

        // 执行登录
        $loginResponse = $this->performLogin($requestParams);

        // 缓存token
        $token = $loginResponse['data']['token'];
        $this->cacheToken($username, $token);

        return $token;
    }

    /**
     * 从缓存获取token
     *
     * @param string $username 用户名
     * @return string|null token或null
     */
    protected function getTokenFromCache(string $username): ?string
    {
        $key = self::REDIS_KEY_PREFIX . $username;
        return Redis::get($key);
    }

    /**
     * 从数据库获取用户信息
     *
     * @param string $username 用户名
     * @return array 用户信息
     * @throws ApiException 用户不存在异常
     */
    protected function getUserFromDatabase(string $username): array
    {
        $user = UserModel::getOne(['account' => $username], ['account', 'password', 'id']);

        if (!(isset($user['id']) && $user['id'] > 0)) {
            throw new ApiException('用户不存在');
        }

        return $user;
    }

    /**
     * 准备登录参数
     *
     * @param array $user 用户信息
     * @return array 登录参数
     */
    protected function prepareLoginParams(array $user): array
    {
        return [
            'scene'        => 'account',
            'account'      => $user['account'],
            'password' => $user['password'],
        ];
    }

    /**
     * 执行登录
     *
     * @param array $params 登录参数
     * @return array 登录响应
     * @throws ApiException 登录失败异常
     */
    protected function performLogin(array $params): array
    {
        $data = $this->post('/api/login', $params);
        if (!isset($data['data']['token']) || empty($data['data']['token'])) {
            throw new ApiException('用户登录失败：' . $data['message'] ?? '未知错误');
        }

        return $data;
    }

    /**
     * 缓存token
     *
     * @param string $username 用户名
     * @param string $token token值
     */
    protected function cacheToken(string $username, string $token): void
    {
        $key = self::REDIS_KEY_PREFIX . $username;
        Redis::setEx($key, self::TOKEN_CACHE_TTL, $token);
    }
}
